/**
 * Copyright (c) 2018-2025, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 * 
 * Contains: sched trace event
 * 
 * Change Logs:
 * Date           Author            Notes
 * 2025-06-24     JasonHu           Init
 */

#include <base/trace.h>
#include <trace/sched.h>

#define NX_LOG_NAME "trace-sched"
#define NX_LOG_LEVEL NX_LOG_DBG
#include <base/log.h>

#ifdef CONFIG_TRACE_EVENT_SCHED

#define SYS_TRACE_EVENT_NAME "Sched"

NX_PRIVATE NX_TraceEvent *SchedTraceEvent;

NX_PRIVATE NX_EVENT_SOURCE_DEFINE(SchedSwitch);
NX_PRIVATE NX_EVENT_SOURCE_DEFINE(SchedWakeup);

#define TRACE_SCHED_SWITCH_REGS_NR (3)
#define TRACE_SWITCH_WAKEUP_REGS_NR (2)

#define TRACE_SCHED_KW_SWITCH 1
#define TRACE_SCHED_KW_WAKEUP 2

NX_Error NX_TraceSchedSwitch(NX_U32 prevTid, NX_U32 nextTid, NX_U32 prevStat)
{
    NX_EVENT_HEADER_DEFINE(hdr);
    NX_UArch regs[TRACE_SCHED_SWITCH_REGS_NR];

    if (NX_EventSourceDisabled(NX_EVENT_SOURCE_STATIC(SchedSwitch)))
    {
        return NX_EAGAIN;
    }

    hdr.level = 1;
    hdr.task = NX_ThreadSelf()->tid;
    hdr.version = 1;
    hdr.keyword = TRACE_SCHED_KW_SWITCH;

    regs[0] = prevTid;
    regs[1] = nextTid;
    regs[2] = prevStat;
    
    return NX_TraceEventWrite(NX_EVENT_SOURCE_STATIC(SchedSwitch), &hdr, regs, sizeof(regs));
}

NX_Error NX_TraceSchedWakeup(NX_U32 tid, NX_U32 targetCpu)
{
    NX_EVENT_HEADER_DEFINE(hdr);
    NX_UArch regs[TRACE_SWITCH_WAKEUP_REGS_NR];

    if (NX_EventSourceDisabled(NX_EVENT_SOURCE_STATIC(SchedWakeup)))
    {
        return NX_EAGAIN;
    }

    hdr.level = 1;
    hdr.task = NX_ThreadSelf()->tid;
    hdr.version = 1;
    hdr.keyword = TRACE_SCHED_KW_WAKEUP;

    regs[0] = tid;
    regs[1] = targetCpu;
    
    return NX_TraceEventWrite(NX_EVENT_SOURCE_STATIC(SchedWakeup), &hdr, regs, sizeof(regs));
}

void NX_InitSchedTraceEvent(void)
{
    SchedTraceEvent = NX_TraceEventCreate(SYS_TRACE_EVENT_NAME, 65536);
    if (!SchedTraceEvent)
    {
        NX_LOG_E("Create trace event %s failed!", SYS_TRACE_EVENT_NAME);
        return;
    }
    NX_TraceEventEnable(SchedTraceEvent);
    NX_LOG_I("Create trace event %s suceess!", SYS_TRACE_EVENT_NAME);

    if (NX_EventSourceRegister(NX_EVENT_SOURCE_STATIC(SchedSwitch), SYS_TRACE_EVENT_NAME) != NX_EOK)
    {
        NX_TraceEventDestroy(SchedTraceEvent);
        SchedTraceEvent = NX_NULL;
        return;
    }
    NX_EventSourceEanble(NX_EVENT_SOURCE_STATIC(SchedSwitch), NX_True);
    NX_LOG_I("Create event suorce SchedSwitch suceess!");

    if (NX_EventSourceRegister(NX_EVENT_SOURCE_STATIC(SchedWakeup), SYS_TRACE_EVENT_NAME) != NX_EOK)
    {
        NX_EventSourceUnregister(NX_EVENT_SOURCE_STATIC(SchedWakeup));
        NX_TraceEventDestroy(SchedTraceEvent);
        SchedTraceEvent = NX_NULL;
        return;        
    }
    NX_EventSourceEanble(NX_EVENT_SOURCE_STATIC(SchedWakeup), NX_True);
    NX_LOG_I("Create event suorce SchedWakeup suceess!");
}

void NX_ExitSchedTraceEvent(void)
{
    if (!SchedTraceEvent)
    {
        NX_LOG_I("The trace event %s invalid", SYS_TRACE_EVENT_NAME);
        return;
    }

    NX_EventSourceUnregister(NX_EVENT_SOURCE_STATIC(SchedSwitch));
    NX_EventSourceUnregister(NX_EVENT_SOURCE_STATIC(SchedWakeup));

    if (NX_TraceEventDestroy(SchedTraceEvent) != NX_EOK)
    {
        NX_LOG_E("Destroy trace event %s failed!", SYS_TRACE_EVENT_NAME);
    }
}
#endif